import Watcher from './Watcher'

export default class Compile {
  // 获得挂载点和实例
  constructor(el, vue) {
    // vue:{el: '#app', data: {…}, watch: {…}}
    // vue实例
    this.$vue = vue
    // 挂载点
    this.$el = document.querySelector(el)

    // 用户传入了挂载点
    if (this.$el) {
      // 调用函数,让节点变为fragment,类似于mustache中的tokens
      // 实际上在vue中用的AST,这里写的是轻量级的fragment
      let $fragment = this.node2Fragment(this.$el)
      // 编译
      this.comple($fragment)
      // 替换好的内容上树
      this.$el.appendChild($fragment)
    }
  }
  node2Fragment(el) {
    var fragment = document.createDocumentFragment()
    var child
    // 让所有的DOM都进入 fragment
    while ((child = el.firstChild)) {
      fragment.appendChild(child)
    }
    return fragment
  }
  // 在Vue中,这里是虚拟dom和diff
  comple(el) {
    // 得到子元素
    var childNodes = el.childNodes // [text, h3, text, ul, text]
    var self = this

    var reg = /\{\{(.*)\}\}/

    childNodes.forEach(node => {
      // text 就是节点中的内容
      var text = node.textContent
      // console.dir(node)
      if (node.nodeType == 1) {
        self.compileElement(node)
      } else if (node.nodeType == 3 && reg.test(text)) {
        let name = text.match(reg)[1]
        self.compileText(node, name)
      }
    })
  }
  compileElement(node) {
    var nodeAttrs = node.attributes
    // 类数组对象变为数组
    Array.prototype.slice.call(nodeAttrs).forEach(attr => {
      //分析指令
      var attrName = attr.name
      var value = attr.value
      // 指令都是v-开头的
      var dir = attrName.substring(2)
      var self = this
      // 判断是不是指令
      if (attrName.indexOf('v-') == 0) {
        // v-开头的就是指令
        if (dir == 'model') {
          new Watcher(self.$vue, value, value => {
            node.value = value
          })
          var v = this.getVueVal(self.$vue, value)
          node.value = v
          // 添加监听
          node.addEventListener('input', e => {
            var newVal = e.target.value
            this.setVueVal(self.$vue, value, newVal)
            v = newVal
          })
        }
      }
    })
  }
  compileText(node, name) {
    // console.log(this.getVueVal(this.$vue, name))
    node.textContent = this.getVueVal(this.$vue, name)
    new Watcher(this.$vue, name, value => {
      node.textContent = value
    })
  }

  getVueVal(vue, exp) {
    let val = vue._data
    exp = exp.split('.')
    exp.forEach(k => {
      val = val[k]
    })
    return val
  }

  setVueVal(vue, exp, value) {
    var val = vue
    exp = exp.split('.')
    exp.forEach((k, i) => {
      if (i < exp.length - 1) {
        val = val[k]
      } else {
        val[k] = value
      }
    })
  }
}
